﻿#pragma warning(disable:4661)
#include <map>
//#include "boost/multi_array.hpp"
//#include "boost/numeric/ublas/matrix.hpp"
#include "TalairachVolume.h"
#include "IcemVolume.h"
#include "IcemStack.h"
#include <ctime>

//#include "CudaIcemVolume.h"
#include "cuda.h"
#include "cuda_runtime.h"

using namespace std;
using namespace std::tr1;
//using namespace boost;
//using namespace boost::numeric::ublas;

template<class T>
IcemStack<T>::~IcemStack(){
	cudaFree(renderedVolume.ptr);
	for (int i=0;i<3;++i)
		cudaFree(cudaImage[i]);
}

template<class T>
void IcemStack<T>::AddVolume(int id, shared_ptr<IcemVolume<T> > volume) {

	VolumeId vid = static_cast<VolumeId>(id);
	if (volumeStack.find(vid) != volumeStack.end())
		volumeStack.erase(vid);
	volumeStack.insert(pair<VolumeId,shared_ptr<IcemVolume<T> > >(vid,volume));
	auto it = volumeStack.begin();
	it->second->GetMinCoordinates(minCoordinates);
	it->second->GetSize(size);
	for (int dir=0;dir<3;++dir) {
		size_t newI,newJ;
		switch(dir) {
		case 0:
			newI = size[0];
			newJ = size[1];
			break;
		case 1:
			newI = size[0];
			newJ = size[2];
			break;
		case 2:
			newI = size[2];
			newJ = size[1];
			break;
		}
		cudaFree(cudaImage[dir]);
		int ret = cudaMalloc(&(cudaImage[dir]), newI * newJ * sizeof(uchar3));
	}
	for (auto it=volumeStack.begin();it!=volumeStack.end();++it)
		it->second->SetSliceSize(size);

    //new stuff
	cudaExtent extent = make_cudaExtent(size[0]*sizeof(uchar3),size[1],size[2]);
	int ret = cudaMalloc3D(&renderedVolume,extent);
}

template<class T>
void IcemStack<T>::AddTalairachVolume(shared_ptr<TalairachVolume> volume, bool filtered) {
	volume->SetSliceSize(size);
	if (!filtered)
		talairach = volume;
	else
		filteredTalairach = volume;
}



template<class T>
int IcemStack<T>::GetSliceSize(int dir, int* sizeX, int* sizeY){
	auto it = volumeStack.begin();
	it->second->GetSliceSize(dir, sizeX, sizeY);
	return 0;
}

template<class T>
void IcemStack<T>::SetFilterLimits(int id,float lowerLimit,float upperLimit){
 VolumeId vid = static_cast<VolumeId>(id);
 if (volumeStack.find(vid) != volumeStack.end()) {
		volumeStack[vid]->SetFilterLimits(lowerLimit, upperLimit);
	}
}


template<class T>
void IcemStack<T>::SetTransformationMatrix (int id, float mat[4][4]){
 VolumeId vid = static_cast<VolumeId>(id);
 if (volumeStack.find(vid) != volumeStack.end()) {
		volumeStack[vid]->SetTransformationMatrix(mat);
	}
}


template<class T>
void IcemStack<T>::GetVolumesNumberAndSize(int* N, int* X, int* Y, int* Z){
	*N = volumeStack.size();
	*X = size[0]/2;
	*Y = size[1]/2;
	*Z = size[2]/2;
}

template<class T>
void IcemStack<T>::GetRenderedVolumes(unsigned char* volumes, const char*** names, double** transformationMatrix){
	unsigned char* v=volumes;
	static const char * EnumStrings[] = {"mri","pet","stat","eeg","ct","mask","ctWithElectrodes","atlas","filteredAtlas","fMRI"};
	*names = (const char**)malloc(volumeStack.size()*sizeof(char*));
	//*transformationMatrix = (double*)malloc(16*sizeof(double));
	for (int i=0;i<4;++i) {
		for (int j=0;j<4;++j) {
			(*transformationMatrix)[4*i+j] = 0;
		}
		(*transformationMatrix)[4*i+i] = 1;
	}
	for (int i=0;i<3;++i) {
		(*transformationMatrix)[i+12] = minCoordinates[i];
	}
	int i=0;
	for (auto it=volumeStack.begin();it!=volumeStack.end();++it) {
		bool interp = true;
		VolumeId id = it->first;
		(*names)[i++]=EnumStrings[id];
		if (id == mask || id == stat || id == fMRI){
			interp = false;
        }
		it->second->GetRenderedVolume(v,minCoordinates, size, interp);
		v += size[0]*size[1]*size[2]/8;
	}
}

template class IcemStack<uint8_t>;